using System;
using System.IO;
using System.Drawing;
using System.Collections;
using System.Xml.Serialization;
using System.Xml.Xsl;
using System.Xml.XPath;
using System.Xml;

namespace G2007.CodeGen.Generator
{

	/// <summary>
	/// Compiles the file
	/// </summary>
	public class Compiler
	{
		private Compiler()
		{

		}

		private string inputFile;
		private main @main;

		private string[] data=null;


		Data.HeaderInfo hi;

		Data.Class[] classes;
		Data.Enum[] enums;
		

		public Compiler(string inputFile, main @main) : this() 
		{
			this.inputFile=inputFile;
			this.main=@main;
		}


		public bool Compile(out string error, bool run) 
		{
			error="";
			if(!File.Exists(inputFile)) 
			{
				error="File " + inputFile + " does not exist";
				return false;
			}


			data=File.OpenText(inputFile).ReadToEnd().Split('\n');



			if(!getHeaders(out error)) return false;
			else 
			{
				main.AppendText(this,hi.ToString(),Color.DarkGreen);
			}

			if(!getEnums(out error)) return false;
			else 
			{
				foreach(Data.Enum @enum in enums) 
				{
					if(!@enum.GenerateFields(out error)) 
					{
						main.AppendText(this,"GenerateEnumError " + error,Color.Red);
						return false;
					} 
					else 
					{
						main.AppendText(this,@enum.ToString(),Color.Orange);
					}

				}
			}


			if(!getClasses(out error)) return false;
			else 
			{
				
				foreach(Data.Class @class in classes) 
				{
					if(!@class.GenerateFields(out error)) 
					{
						main.AppendText(this,"GenerateFieldsError " + error,
							Color.Red); 
						return false;
					}
					else main.AppendText(this,@class.ToString()+"\n\n",
						Color.DarkGreen);
				}

				
				// in each class we need to identify if there are any
				// other classes which have an array of this object
				// if they do then we need to add code to delete that object 
				// from the database

				foreach(Data.Class @class in classes) 
				{
					ArrayList fields=new ArrayList();
					foreach(Data.Class otherClass in classes) 
					{
						foreach(Data.Class.ClassAttributes ca in otherClass.ClassAttribs) 
						{
							if(ca.CType==Data.Class.ClassType.array && ca.Type.Equals(@class.Name)) 
							{
								// this is an array of the same type of the @class object, we need to add this
								// to the class list that we shall be deleting
								fields.Add(new Data.Class.DeleteArray(ca.Name,ca.Type,otherClass.Name));
							}
						}
					}
					@class.DeleteArrayObjs=(Data.Class.DeleteArray[])fields.ToArray(typeof(Data.Class.DeleteArray));
				}

				
			}


			// ok by this stage we have all of the classes sorted and the hi information which is just great
			// now we need to generate some XML

            main.SetProgress(enums.Length + classes.Length + 4);

			XmlSerializer xmls;
			MemoryStream ms;
			XmlDocument xmlDoc;
			StringWriter sw;
			int x;

			string val;

			if(run) 
			{
				if(!Directory.Exists(hi.Output))
					Directory.CreateDirectory(hi.Output);
			}

			foreach(Data.Enum @enum in enums) 
			{
				xmls=new XmlSerializer(typeof(Data.Enum));
				ms=new MemoryStream();
				xmls.Serialize(ms,@enum);
				ms.Position=0;
				xmlDoc=new XmlDocument();
				xmlDoc.Load(ms);
				sw=new StringWriter();
				xmlDoc.Save(sw);
				main.AppendText(this,sw.ToString(),Color.Orange);

				x=0;

				val=System.Configuration.ConfigurationSettings.AppSettings["ENUMGEN[" + x + "]"];
				if(val==null || val=="") break;
				main.AppendText(this,transform(sw.ToString(),val.Split('|')[0]),Color.DarkBlue);

				if(run) 
				{
					if(File.Exists(hi.Output + "\\" + val.Split('|')[1].Replace("[x]",@enum.Name))) 
					{
						File.Delete(hi.Output + "\\" + val.Split('|')[1].Replace("[x]",@enum.Name));
					}
					StreamWriter sw2=File.CreateText(hi.Output + "\\" + val.Split('|')[1].Replace("[x]",@enum.Name));
					sw2.Write(transform(sw.ToString(),val.Split('|')[0]));
					sw2.Close();
				}
				x++;
                main.AddProgress();
			}

			foreach(Data.Class @class in classes) 
			{
				xmls=new XmlSerializer(typeof(Data.Class));
				ms=new MemoryStream();
				xmls.Serialize(ms,@class);
				ms.Position=0;
				xmlDoc=new XmlDocument();
				xmlDoc.Load(ms);
				sw=new StringWriter();
				xmlDoc.Save(sw);
				main.AppendText(this,sw.ToString(),Color.Brown);


				x=0;
				while(true) 
				{
					val=System.Configuration.ConfigurationSettings.AppSettings["CLASSGEN[" + x + "]"];
					if(val==null || val=="") break;
					main.AppendText(this,transform(sw.ToString(),val.Split('|')[0]),Color.DarkKhaki);

					if(run) 
					{
						if(File.Exists(hi.Output + "\\" + val.Split('|')[1].Replace("[x]",@class.Name))) 
						{
							File.Delete(hi.Output + "\\" + val.Split('|')[1].Replace("[x]",@class.Name));
						}
						StreamWriter sw2=File.CreateText(hi.Output + "\\" + val.Split('|')[1].Replace("[x]",@class.Name));
						sw2.Write(transform(sw.ToString(),val.Split('|')[0]));
						sw2.Close();
					}
					x++;
				}
                main.AddProgress();
			}


			xmls=new XmlSerializer(typeof(Data.HeaderInfo));
			ms=new MemoryStream();
			xmls.Serialize(ms,hi);
			ms.Position=0;
			xmlDoc=new XmlDocument();
			xmlDoc.Load(ms);
			sw=new StringWriter();
			xmlDoc.Save(sw);
			main.AppendText(this,sw.ToString(),Color.Brown);

			x=0;
			while(true) 
			{
				val=System.Configuration.ConfigurationSettings.AppSettings["HEADERGEN[" + x + "]"];
				if(val==null || val=="") break;
				main.AppendText(this,transform(sw.ToString(),val.Split('|')[0]),Color.DarkBlue);
				if(run) 
				{
					if(File.Exists(hi.Output + "\\" + val.Split('|')[1])) 
					{
						File.Delete(hi.Output + "\\" + val.Split('|')[1]);
					}

					StreamWriter sw2=File.CreateText(hi.Output + "\\" + val.Split('|')[1]);
					sw2.Write(transform(sw.ToString(),val.Split('|')[0]));
					sw2.Close();
				}				
				x++;

			}

            main.AddProgress();

            x = 0;               
            HeaderAndClass handc=new HeaderAndClass();
            handc.HeaderInfo=hi;
            handc.Classes=classes;
            xmls=new XmlSerializer(typeof(HeaderAndClass));
            ms=new MemoryStream();
            xmls.Serialize(ms,handc);
            ms.Position=0;
            xmlDoc=new XmlDocument();
            xmlDoc.Load(ms);
            sw=new StringWriter();
            xmlDoc.Save(sw);

            main.AppendText(this, sw.ToString(),Color.BlueViolet);

            while (true)
            {
                val = System.Configuration.ConfigurationSettings.AppSettings["HEADERANDCLASSGEN[" + x + "]"];
                if (val == null || val == "") break;

                main.AppendText(this, transform(sw.ToString(), val.Split('|')[0]),Color.Chocolate);

                if (run)
                {
                    if (File.Exists(hi.Output + "\\" + val.Split('|')[1]))
                    {
                        File.Delete(hi.Output + "\\" + val.Split('|')[1]);
                    }
                    StreamWriter sw2 = File.CreateText(hi.Output + "\\" + val.Split('|')[1]);
                    sw2.Write(transform(sw.ToString(), val.Split('|')[0]));
                    sw2.Close();
                }
                x++;

             }

             main.AddProgress();

			x=0;
			while(true) 
			{
				val=System.Configuration.ConfigurationSettings.AppSettings["ALLGEN[" + x + "]"];
				if(val==null || val=="") break;

				// if we have any of these then lets do this

				string allData="";
				foreach(Data.Class @class in classes) 
				{
					xmls=new XmlSerializer(typeof(Data.Class));
					ms=new MemoryStream();
					xmls.Serialize(ms,@class);
					ms.Position=0;
					xmlDoc=new XmlDocument();
					xmlDoc.Load(ms);
					sw=new StringWriter();
					xmlDoc.Save(sw);

					allData+=transform(sw.ToString(),val.Split('|')[0]);

				}

				main.AppendText(this,allData,Color.Purple);

				if(run) 
				{
					if(File.Exists(hi.Output + "\\" + val.Split('|')[1])) 
					{
						File.Delete(hi.Output + "\\" + val.Split('|')[1]);
					}
					StreamWriter sw2=File.CreateText(hi.Output + "\\" + val.Split('|')[1]);
					sw2.Write(allData);
					sw2.Close();
				}
				x++;
			}

            main.AddProgress();


            new PageGenerator().DoPageWork(data, classes);

            main.AddProgress();



			return true;

		}


        public class HeaderAndClass
        {
            public HeaderAndClass() { }
            public Data.HeaderInfo HeaderInfo;
            public Data.Class[] Classes;
        }

        protected string transform(string inputxml, string filename)
        {            
            XsltArgumentList args=new XsltArgumentList();
            args.AddParam("DOTNET2", "", main.chkDotNet20.Checked ? "true" : "false");
            return transform(inputxml, filename, args);

        }

		protected string transform(string inputxml,string filename, XsltArgumentList args) 
		{
			XslTransform xslt=new XslTransform();
			
            
            

#if(RELEASE)
           encrypter.enc.Decrypt(null,main.AppPath + "\\" + filename,"ghtik123",
                System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(
                                 "G2007.CodeGen.bin.Debug.XSLT." + filename + ".enc"));

           xslt.Load(main.AppPath + "\\" + filename);
#else

            xslt.Load(main.AppPath + "\\XSLT\\" + filename);

#endif

            
            XPathDocument xpath=new XPathDocument(new XmlTextReader(new StringReader(inputxml)));
			StringWriter sw_parsed=new StringWriter();
			xslt.Transform(xpath,args,sw_parsed);

            File.Delete(main.AppPath + "\\" + filename);

			return sw_parsed.ToString().Replace("<?xml version=\"1.0\" encoding=\"utf-16\"?>","").Replace("&amp;","&").Replace("&gt;",">").Replace("&lt;","<")
                .Replace("<META http-equiv=\"Content-Type\" content=\"text/html; charset=utf-16\">","");


            
		}



		protected bool getEnums(out string error) 
		{
			error="";
			ArrayList al=new ArrayList();
			foreach(string s in data) 
			{
				if(s.ToUpper().StartsWith("ENUM")) 
				{
					if(s.Split('\t').Length>1) 
						al.Add(new Data.Enum(s.Split('\t')[1].Trim(),hi,data));
				}
			}
			this.enums=(Data.Enum[])al.ToArray(typeof(Data.Enum));
			return true;
		}

		protected bool getClasses(out string error) 
		{
			error="";
			ArrayList al=new ArrayList();
			foreach(string s in data) 
			{
				if(s.ToUpper().StartsWith("CLASS")) 
				{
					if(s.Split('\t').Length>1) 
						al.Add(new Data.Class(s.Split('\t')[1].Trim(),
							hi,data,
							(Data.Class[])al.ToArray(typeof(Data.Class)),enums));
				}
			}
			this.classes=(Data.Class[])al.ToArray(typeof(Data.Class));
			return true;
		}


		protected bool getHeaders(out string error) 
		{
			string pi,dc,output,prefix;
			string val="";
			error="";

			if(getVariable("project",out val)) 
			{
				pi=val;	
			} 
			else 
			{
				error="No project name";
				return false;
			}


			if(getVariable("database",out val)) 
			{
				dc=val;	
			} 
			else 
			{
				error="No database name";
				return false;
			}


			if(getVariable("output",out val)) 
			{
				output=val;	
			} 
			else 
			{
				error="No output name";
				return false;
			}


			if(getVariable("prefix",out val)) 
			{
				prefix=val;
			} 
			else 
			{
				error="No prefix name";
				return false;
			}

			hi=new G2007.CodeGen.Data.HeaderInfo(pi.Trim(),output.Trim(),dc.Trim(),prefix.Trim());
			
			return true;
		}



		protected bool getVariable(string name, out string @value) 
		{
			@value="";
			foreach(string s in data) 
			{
				if(s.ToUpper().StartsWith(name.ToUpper())) 
				{
					if(s.Split('\t').Length>1) {
						@value=s.Split('\t')[1];
						return true;
					} else return false; 
				}
			}
			return false;
		}


	}
}
